package jastgen.generator;

import static jastgen.generator.Helper.*;
import static jastgen.generator.Generator.*;

import java.io.PrintWriter;
import java.util.*;

import jastgen.CompileException;
import jastgen.nodes.ClassDef;
import jastgen.nodes.Field;
import jastgen.nodes.Module;
import jastgen.nodes.Type;

// NodeReplacerはVisitorAdapterに改名する?
class NodeReplacerBodyGenerator implements BodyGenerator {

	private final List<String> visitParamTypes;
	private final Set<String> nodeTypes;
	private final Module node;

	NodeReplacerBodyGenerator(
			final List<String> visitParamTypes,
			final Set<String> nodeTypes,
			final Module node
		) throws CompileException {

		this.visitParamTypes = visitParamTypes;
		this.nodeTypes = nodeTypes;
		this.node = node;
	}

	public void generate(
			final PrintWriter out)
	{
		generateVisitorBody(out, node);
	}

	private void generateVisitorBody(
			final PrintWriter out,
			final Module node)
	{
		out.println("/**");
		out.println(" * ");
		out.println(" */");

		out.println("public abstract class NodeReplacer extends VisitorImpl<");

		boolean is1st = true;
		for(final String s : visitParamTypes) {
			if(!is1st) out.println(",");
			is1st = false;
			out.print("		" + s);
		}

		out.println(">");
		out.println("{");

		out.println("	private final Context context;");
		out.println();
		out.println("	public NodeReplacer(final Context context) {");
		out.println("		this.context = context;");
		out.println("	}");

		// クラスごとのメソッドを出力
		for(final ClassDef i : node.getClassDefs())
			printClassDef(out, i);

		// クラスごとのメソッドを出力
		for(final ClassDef i : node.getClassDefs())
			printBuildNode(out, i);

		out.println("}");
	}

	private void printBuildNode(
			final PrintWriter out,
			final ClassDef c)
	{
		final String context = unique(c, "context");

		final String t = c.getInterfaceOrClassName();
		final String n = c.getBaseName();
		final String p = c.getTypeParamsString();

		out.println();
		out.println("	/**");
		out.println("	 * Override this method if you want to replace " + n + " node.");
		out.println("	 */");
		out.println("	protected " + p + t + " replace" + n + "(");

		out.print("			final Context " + context);
		for(final Field f : c.getFields()) {
			if(f.hasBody())
				continue;
			out.println(",");
			out.print("			final " + f.getType() + " " + f.getName());
		}
		out.println(")");

		out.println("	{");

		out.println("		return " + n + ".get(");
		out.print("				" + context);

		for(final Field f : c.getFields()) {
			if(f.hasBody())
				continue;
			out.println(",");
			out.print("				");
			out.print(f.getName());
		}

		out.println(");");

		out.println("	}");
	}

	private void printClassDef(
			final PrintWriter out,
			final ClassDef i)
	{
		final String bn = i.getBaseName();

		out.println();
		out.println("	/**");
		out.println("	 * " + bn + "ノードのフィールドを処理した結果を引数としてreplace" + bn + "を呼びだす。");
		out.println("	 * 可能な場合はこのメソッドではなくreplace" + bn + "のオーバーライドを検討されたし。");
		out.println("	 */");

		final String retTy = i.getInterfaceOrClassName();
		printVisitC(out, i, "in", retTy, true);
		out.println("	{");

		out.println("		return replace" + bn + "(");
		out.print("				context");

		for(final Field f : i.getFields()) {
			if(f.hasBody())
				continue;
			out.println(",");
			out.print("				");
			generateVisitorBodyMember(out, f);
		}

		out.println(");");

		out.println("	}");
	}

	// VoidVisitorImpl、VoidVisitorAdapter、VisitorImpl、NodeReplacer、TreePrinterの5つは大部分が共通
	private void generateVisitorBodyMember(
			final PrintWriter out,
			final Field node)
	{
		final String n = node.getNameInMethod();
		final Type type = node.getType();

		if(type.maybeContainerToVisit()) {
			final String inner = type.getOnlyParam().toString();
			if(nodeTypes.contains(inner)) {
				// 型パラメータが1つで、ノードクラスであり、
				// 外側のクラスはcontainerPrefixesに含まれる場合。
				out.print("in" + inner + "(node.get" + n + "())");
				return;
			}
		}

		final String t = type.toString();
		if(nodeTypes.contains(t)) {
			out.print("in" + type + "(node.get" + n + "())");
			return;
		}

		out.print("node." + getOrIs(node) + n + "()");
	}
}
